﻿using Couchbase;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GuoZhiQi.MiddleCache
{
    public sealed class CouchBasePool
    {
        #region Constants

        private const string DefaultPool = "default";

        #endregion Constants

        #region Properties

        private ConcurrentDictionary<string, BucketProperties> _bucketProps;
        /// <summary>
        /// 结构    bucket的属性
        /// </summary>
        public struct BucketProperties
        {
            public string BucketName;
            public string BucketPassword;
        }

        /// <summary>
        /// Bucket name for the client.
        /// </summary>
        public ConcurrentDictionary<string, BucketProperties> PoolProperties
        {
            get
            {
                if (_bucketProps == null)
                    return new ConcurrentDictionary<string, BucketProperties>();

                return _bucketProps;
            }
        }

        #endregion Properties

        #region Membase client.

        private ConcurrentDictionary<string, CouchbaseClient> _client;
        private DateTime _lastRecycle;

        /// <summary>
        /// 默认的couchBaseClient客户端
        /// </summary>
        public CouchbaseClient Client
        {
            get
            {
                if (this._client == null || this._client.Count == 0)
                    return null;

                this.RecycleOldClients();

                if (this._client.ContainsKey(CouchBasePool.DefaultPool))
                    return this._client[CouchBasePool.DefaultPool];
                else
                    throw new NotSupportedException("This operation is supported only when there is 'default' bucket pool present");
            }
        }

        /// <summary>
        /// Currently active clients and their buckets.
        /// 当前可用的client列表
        /// </summary>
        public ConcurrentDictionary<string, CouchbaseClient> ClientList
        {
            get
            {
                if (this._client == null || this._client.Count == 0)
                    return null;

                this.RecycleOldClients();

                return this._client;
            }
        }

        /// <summary>
        /// Returns client by bucket.
        /// </summary>
        /// <param name="vBucket">Bucket name.</param>
        /// <returns>Returns client for selected bucket.</returns>
        public CouchbaseClient GetClient(string vBucket)
        {
            if (this._client.ContainsKey(vBucket))
            {
                this.RecycleOldClients();
                return _client[vBucket];
            }
            else
                return null;
        }

        /// <summary>
        /// Sets or gets maximum alive time for all clients.
        /// 设置client的最大活跃时间
        /// </summary>
        public TimeSpan RecycleAfter { get; set; }

        #endregion Membase client.

        #region Constructor.

        /// <exception cref="ArgumentNullException">Null exception</exception>
        /// <summary>
        /// Instantiates a new membase pool with recycle period of 4 hours, default bucket and empty password. Heavy object!
        /// 初始化一个couchbase  pool    回收时间为4个小时   默认的bucket和空的密码
        /// </summary>
        public CouchBasePool()
            : this(CouchBasePool.DefaultPool, "")
        {
        }

        /// <exception cref="ArgumentNullException">Null exception</exception>
        /// <summary>
        /// Instantiates a new membase pool with recycle period of 4 hours. Heavy object!
        /// 初始化一个couchbase pool     回收时间为4个小时
        /// </summary>
        /// <param name="bucket">Bucket to connect to.</param>
        /// <param name="password">Password for the bucket.</param>
        public CouchBasePool(string bucket, string password)
            : this(bucket, password, new TimeSpan(4, 0, 0))
        {
        }

        /// <exception cref="ArgumentNullException">Null exception</exception>
        /// <summary>
        /// Instantiates a new membase pool with custom recycle period. Heavy object!
        /// 初始化一个couchbase  pool   自定义的时间   如果bucket  为null  则为默认的default
        /// </summary>
        /// <param name="bucket">Bucket to connect to.</param>
        /// <param name="password">Password for the bucket.</param>
        /// <param name="maximumClientUptime">Sets maximum uptime for pool before it recycles.</param>
        public CouchBasePool(string bucket, string password, TimeSpan maximumClientUptime)
        {
            this._client = new ConcurrentDictionary<string, CouchbaseClient>();
            ///最后循环时间为当前时间
            this._lastRecycle = DateTime.Now;
            ///过期时间设置
            this.RecycleAfter = maximumClientUptime;

            BucketProperties propItem = new BucketProperties();

            if (bucket == null)
            {
                ///默认的couchbase  client客户端
                this._client.TryAdd(CouchBasePool.DefaultPool, new CouchbaseClient());
                propItem.BucketName = CouchBasePool.DefaultPool;
                propItem.BucketPassword = "";
            }
            else
            {
                this._client.TryAdd(bucket, new CouchbaseClient(bucket, password));
                propItem.BucketName = bucket;
                propItem.BucketPassword = password;
            }

            this._bucketProps = new ConcurrentDictionary<string, BucketProperties>();
            this._bucketProps.TryAdd(propItem.BucketName, propItem);
        }

        #endregion Constructor.

        #region AddBucket.

        /// <summary>
        /// Adds client to current pool
        /// 添加一个client到当前的client列表
        /// </summary>
        /// <param name="bucket">Name of the bucket to add.</param>
        /// <param name="password">Password of the bucket.</param>
        /// <returns>True if bucket was added.</returns>
        public bool AddBucketClient(string bucket, string password)
        {
            BucketProperties propItem = new BucketProperties();

            if (this._client == null)
            {
                this._client = new ConcurrentDictionary<string, CouchbaseClient>();
                this._bucketProps = new ConcurrentDictionary<string, BucketProperties>();

                if (bucket == null)
                {
                    if (!this._client.TryAdd(CouchBasePool.DefaultPool, new CouchbaseClient()))
                        return false;

                    propItem.BucketName = CouchBasePool.DefaultPool;
                    propItem.BucketPassword = "";
                    this._bucketProps.TryAdd(propItem.BucketName, propItem);
                }
                else
                {
                    if (!this._client.TryAdd(bucket, new CouchbaseClient(bucket, password)))
                        return false;

                    propItem.BucketName = bucket;
                    propItem.BucketPassword = password;
                    this._bucketProps.TryAdd(propItem.BucketName, propItem);
                }
            }
            else
            {
                ///如果添加的bucket为null  并且client列表中不存在default   key
                if (bucket == null && !this._client.ContainsKey(CouchBasePool.DefaultPool))
                {
                    if (!this._client.TryAdd(CouchBasePool.DefaultPool, new CouchbaseClient()))
                        return false;

                    propItem.BucketName = CouchBasePool.DefaultPool;
                    propItem.BucketPassword = "";
                    this._bucketProps.TryAdd(propItem.BucketName, propItem);
                }
                else if (!this._client.ContainsKey(bucket))
                {
                    if (!this._client.TryAdd(bucket, new CouchbaseClient(bucket, password)))
                        return false;

                    propItem.BucketName = bucket;
                    propItem.BucketPassword = password;
                    this._bucketProps.TryAdd(propItem.BucketName, propItem);
                }
                else
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Recycles old clients based on <see cref="RecycleAfter"/> property.
        /// 循环更新所有的client
        /// </summary>
        /// <param name="forceRecyleAll">Forces recycling disregarding the <see cref="RecycleAfter"/> property.</param>
        public void RecycleOldClients(bool forceRecyleAll = false)
        {
            if (DateTime.Now - this._lastRecycle > this.RecycleAfter || forceRecyleAll)
            {
                this._lastRecycle = DateTime.Now;

                foreach (string vBucket in this._bucketProps.Keys)
                {
                    this._client.AddOrUpdate(vBucket, new CouchbaseClient(this._bucketProps[vBucket].BucketName,
                        this._bucketProps[vBucket].BucketPassword),
                        (key, value) =>
                        {
                            return new CouchbaseClient(this._bucketProps[vBucket].BucketName, this._bucketProps[vBucket].BucketPassword);
                        });
                }
            }
        }

        #endregion AddBucket.
    }
}
